package com.mudalov.rec.dao.interfaces;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;

public abstract class HbnGenericDAO<T extends IClonable<T>, ID extends Serializable> implements
		IGenericDAO<T, ID> {

	protected SessionFactory sessionFactory;

	protected abstract Class<? extends Serializable> getEntityClass();

	public List<T> findAll() {
		return findDetachedObjectsByCriteria(new ArrayList<Criterion>());
	}

    public void deleteObject(T object) {
        Session session = sessionFactory.openSession();
		Transaction tx = session.beginTransaction();
		session.delete(object);
		tx.commit();
		session.close();
    }

	public T findById(ID id) {
		Session session = sessionFactory.openSession();
		Transaction tx = session.beginTransaction();
		T result = (T) session.get(getEntityClass(), id);
		tx.commit();
		session.close();
		return result;
	}

	public void save(T object) {
		Session session = sessionFactory.openSession();
		Transaction tx = session.beginTransaction();
		session.save(object);
		tx.commit();
		session.close();
	}

    public void saveOrUpdate(T object) {
		Session session = sessionFactory.openSession();
		Transaction tx = session.beginTransaction();
		session.saveOrUpdate(object);
		tx.commit();
		session.close();
	}

    public void saveOrUpdateAll(Collection<T> objects) {
        Session session = sessionFactory.openSession();
		Transaction tx = session.beginTransaction();
        for (T object : objects) {
            session.saveOrUpdate(object);
        }
		tx.commit();
		session.close();
    }

	public void saveAll(Collection<T> objects) {
        Session session = sessionFactory.openSession();
		Transaction tx = session.beginTransaction();
        for (T object : objects) {
            session.save(object);
        }
		tx.commit();
		session.close();
	}

    public T findDetachedObjectByCriteria(List<Criterion> criterions) {
        List<T> objects = findDetachedObjectsByCriteria(criterions);
		if (objects != null && objects.size() > 0) {
			return objects.get(0);
		} else {
			return null;
		}
    }

    public T findObjectByProperty(String propName, Object value) {
        List<Criterion> criterions = new ArrayList<Criterion>();
        criterions.add(Restrictions.eq(propName, value));
        return findDetachedObjectByCriteria(criterions);
    }

    protected List<T> findDetachedObjectsByCriteria(List<Criterion> criterions) {
        Session session = sessionFactory.openSession();
        List<T> objects = findObjectsByCriteria(session, criterions);
        List<T> transferObjects = new ArrayList<T>();
        for (T object : objects) {
            transferObjects.add(object.getClone());
        }
		session.close();
		return transferObjects;
    }
    

    protected List<T> findObjectsByCriteria(Session session, List<Criterion> criterions) {
        Transaction tx = session.beginTransaction();
        Criteria criteria = session.createCriteria(getEntityClass());
        for (Criterion inputCriterion : criterions) {
            criteria.add(inputCriterion);
        }
        List<T> objects = criteria.list();
		tx.commit();
		return objects;
    }

    public boolean checkForExist(String propertyName, String propertyValue) {
        T object = findObjectByProperty(propertyName, propertyValue);
        return object != null;
    }
    
}
